<?php
/*
Plugin Name: SuperMegaÜberTabs
Description: Like GTTabs only super, mega, and über
Version: 0.4.4
Author: Small Hadron Collider
Author URI: http://www.smallhadroncollider.com/
*/

if (!class_exists('SMUT')) {

    class SMUT {

        /** CONFIG **/

        // Sets the header level (e.g. h2) for the first level of tabs
        static private $first_header_level = 2;

        /***********/


        // Stop anyone instantiating class
        private function __construct(){}
        private function SMUT() {}

        // Keeps track of used 'id's
        static private $used_names = array();

        // Setup SMUT
        static public function setup() {
            // Add content filter
            add_filter('the_content', array('SMUT', 'filter'));

            // Add JavaScript file
            add_action('wp_enqueue_scripts', array('SMUT', 'scripts'));

            // Add CSS file
            add_action('wp_head', array('SMUT', 'css'));
        }

        static public function scripts() {
           wp_enqueue_script('custom_script', plugins_url('/smut-min.js', __FILE__), array('jquery'), '0.1');
        }

        static public function css() {
            echo '<link rel="stylesheet" href="'.plugins_url('/smut.css', __FILE__).'" />';
        }

        static public function filter($c) {
            // Run the loop, starting at level 1
            $c = self::loop($c, 1, 'acc');
            $c = self::loop($c, 1, 'tab');

            return $c;
        }

        // $c is content, $l is the current tab level
        static private function loop($c, $l, $t) {

            // The tab pattern - ain't it pretty
            $pattern = '/^.*\['.$t.':{'.$l.'}([^:][^\]]*)\].*$/im';

            // Find first occurernce of this level.
            // If none found return passed content / If found get first match
            if (!preg_match($pattern, $c, $first_matches, PREG_OFFSET_CAPTURE)) {
                return $c;
            } else {
                $first = $first_matches[0][1];
            }

            // Find current level tab end or show an error message
            if (preg_match('/^.*\['.$t.':{'.$l.'}END\].*$/im', $c, $last_matches, PREG_OFFSET_CAPTURE)) {
                $last = $last_matches[0][1];
            } else {
                die('<div class="smut-error"><p><strong>SuperMegaÜberTabs</strong></p><p>No valid end '.$t.': ['.$t.str_repeat(':', $l).'END]</p><p>Started at "'.$first_matches[1][0].'" '.$t.'</p></div>');
            }

            // Pre becomes anything before first tab
            $pre = substr($c, 0, $first);
            // Post becomes anything after end of tabs
            $post = substr($c, $last+strlen($last_matches[0][0]));

            // $inner becomes anything in between $pre and $post
            $string = substr($c, $first, $last-$first);

            // Get all matches for tabs: goes into $matches, if none found return content to avoid unnecessary work
            if (!preg_match_all($pattern, $string, $matches, PREG_OFFSET_CAPTURE)) {
                return $c;
            }

            // Current header level
            $hl = ($l+self::$first_header_level < 6) ? $l+self::$first_header_level : 6;

            if ($t === 'tab') {
                // Setup link list
                $link_list = '<p class="smut-in-section"><strong>In this section:</strong></p>';
                $link_list .= '<ul class="smut-list">';
            }

            // Inner keeps track of content
            $inner = '';

            $n = count($matches[0]);

            for ($i = 0; $i < $n; $i++) {

                // Title from second set of preg_matches
                $title = $matches[1][$i][0];

                // Get unique nav id
                $nav_id = self::make_nav_id($title);

                // Start of div
                $inner .= "\n".'<div id="'.$nav_id.'" class="smut-'.$t.' level-'.$l.'">';

                // Output header
                // Check for optional header tags {TabName}
                if (preg_match('/^{([^}]+)}/', $title, $result)) {
                    $error  = false;

                    // Check for content between { and }, if not error
                    if (isset($result[1])) {
                        $tab_title = $result[1];
                    } else {
                        $error = true;
                    }

                    // Remove the {xx} part, see what's left - looking for =Header part
                    $edit_title = trim(str_replace($result[0], '', $title));

                    $header_title = '';

                    // If nothing after {xx} then move along (will just hide the header in javascript)
                    if (!empty($edit_title)) {

                        // If there's an equals sign then grab then trim what's after it
                        if (preg_match('/^=\s*(.+)\s*/', $edit_title, $result)) {
                            if (isset($result[1])) {
                                $header_title = $result[1];
                            } else {
                                $error = true;
                            }
                        // If no equals sign at start there's an error in syntax
                        } else {
                            $error = true;
                        }
                    }

                    // If something's gone wrong show a syntax error message
                    if ($error) {
                        die('<div class="smut-error">
                                <p><strong>SuperMegaÜberTabs</strong></p>
                                <p>Invalid name structure: ['.$t.str_repeat(':', $l).$title.']</p>
                                <p>Valid forms:</p>
                                <ul>
                                    <li>TabName (displays as tab name and as the header)</li>
                                    <li>{TabName} (displays as tab name with no header)</li>
                                    <li>{TabName}=Header (displays as tab name with anything after "=" as the header)</li>
                                </ul>
                            </div>');
                    }

                    // Display header: regular title still used here, but swapped out with javascript - so non-JS folk still get the normal header
                    if ($header_title) {
                        $inner .= '<h'.$hl.' class="smut-hidden smut-header level-'.$l.'"><span class="hide">'.$tab_title.': </span>'.$header_title.'</h'.$hl.'>';
                    } else {
                        $inner .= '<h'.$hl.' class="smut-hidden smut-header level-'.$l.'">'.$tab_title.'</h'.$hl.'>';
                    }

                    // Set title to tab_title so the tab gets set to the right thing
                    $title = $tab_title;
                } else {
                    $inner .= '<h'.$hl.' class="smut-header level-'.$l.'">'.$title.'</h'.$hl.'>';
                }

                if ($t === 'tab') {
                    // Add to list of links to precede the div
                    $link_list .= '<li><a href="#'.$nav_id.'">'.$title.'</a></li>';
                }

                // Get substring to itterate over
                $start = $matches[0][$i][1] + strlen($matches[0][$i][0]);
                $end = isset($matches[0][$i+1][1]) ? $matches[0][$i+1][1] : strlen($string)-1;
                $stop = $end - $start;

                // Substring to check for more tabs
                $inner .= self::loop(substr($string, $start, $stop), $l+1, $t);

                // End of div
                $inner .= "\n".'</div>';
            }

            // Add all content before, div start before
            $pre .= "\n\n".'<div class="smut-'.$t.'s level-'.$l.'">';

            if ($t === 'tab') {
                // End link list
                $link_list .= '</ul>';
                // Add link list to pre
                $pre .= $link_list;
            }

            // Add div end and loop through following content for other tabs
            $post = "\n\n".'</div>' . self::loop($post, $l, $t);

            // Combine the pre-tab content, tab content, and post-tab content
            $c = $pre . $inner . $post;

            // Return the tab content
            return $c;
        }

        // Create unique alphanumeric id tags
        static private function make_nav_id($t) {

            // Remove all non alpha-numeric characters (and any end-hyphens)
            $t = htmlspecialchars_decode($t);
            $t = str_replace('&', 'and', $t);
            $t = preg_replace('/[^a-zA-Z0-9]+/', '-', strtolower($t));
            $t = preg_replace('/(^-)|(-$)/', '', $t);
            $title = $t;

            $n = 1;

            // While id is already in use (generally won't run)
            while (in_array($title, self::$used_names)) {
                // Add a number to end of id until becomes unique
                $title = $t.'-'.$n;
                $n++;
            }

            // Add id to list of used 'id's
            self::$used_names[] = $title;

            return $title;
        }
    }
}

// Run the setup scripts
SMUT::setup();